﻿' ******************************************************************************
' ** 
' **  Yahoo! Managed
' **  Written by Marius Häusler 2011
' **  It would be pleasant, if you contact me when you are using this code.
' **  Contact: YahooFinanceManaged@gmail.com
' **  Project Home: http://code.google.com/p/yahoo-finance-managed/
' **  
' ******************************************************************************
' **  
' **  Copyright 2011 Marius Häusler
' **  
' **  Licensed under the Apache License, Version 2.0 (the "License");
' **  you may not use this file except in compliance with the License.
' **  You may obtain a copy of the License at
' **  
' **    http://www.apache.org/licenses/LICENSE-2.0
' **  
' **  Unless required by applicable law or agreed to in writing, software
' **  distributed under the License is distributed on an "AS IS" BASIS,
' **  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
' **  See the License for the specific language governing permissions and
' **  limitations under the License.
' ** 
' ******************************************************************************


Namespace YahooManaged.Finance.API

    ''' <summary>
    ''' Provides methods for downloading quote options.
    ''' </summary>
    ''' <remarks></remarks>
    Public Class QuoteOptionsDownload
        Inherits Base.Download

        ''' <summary>
        ''' Raises if an asynchronous download of quote options completes
        ''' </summary>
        ''' <param name="sender">The event raising object</param>
        ''' <param name="ea">The event args of the asynchronous download</param>
        ''' <remarks></remarks>
        Public Event AsyncDownloadCompleted(ByVal sender As Base.Download, ByVal ea As QuoteOptionsDownloadCompletedEventArgs)

        Friend ReadOnly mFinanceHelper As New FinanceHelper
        Private mXmlParser As New ImportExport.XML


        Public Function Download(ByVal managedID As IID) As QuoteOptionsResponse
            If managedID Is Nothing Then : Throw New ArgumentNullException("managedID", "The passed ID is null.")
            Else : Return Me.Download(managedID.ID)
            End If
        End Function
        Public Function Download(ByVal managedIDs As IEnumerable(Of IID)) As QuoteOptionsResponse
            If managedIDs Is Nothing Then : Throw New ArgumentNullException("managedIDs", "The passed list is null.")
            Else : Return Me.Download(mFinanceHelper.IIDsToStrings(managedIDs))
            End If
        End Function
        Public Function Download(ByVal managedID As IID, ByVal expirationDate As Date) As QuoteOptionsResponse
            If managedID Is Nothing Then : Throw New ArgumentNullException("managedID", "The passed ID is null.")
            Else : Return Me.Download(managedID.ID, expirationDate)
            End If
        End Function
        Public Function Download(ByVal managedIDs As IEnumerable(Of IID), ByVal expirationDate As Date) As QuoteOptionsResponse
            If managedIDs Is Nothing Then : Throw New ArgumentNullException("managedIDs", "The passed list is null.")
            Else : Return Me.Download(mFinanceHelper.IIDsToStrings(managedIDs), expirationDate)
            End If
        End Function
        Public Function Download(ByVal managedID As IID, ByVal expirationDates As IEnumerable(Of Date)) As QuoteOptionsResponse
            If managedID Is Nothing Then : Throw New ArgumentNullException("managedID", "The passed ID is null.")
            Else : Return Me.Download(managedID.ID, expirationDates)
            End If
        End Function

        Public Function Download(ByVal unmanagedID As String) As QuoteOptionsResponse
            If unmanagedID = String.Empty Then : Throw New ArgumentNullException("unmanagedID", "The passed ID is empty.")
            Else : Return Me.ToResponse(MyBase.DownloadStream(Me.DownloadURL(New String() {unmanagedID})))
            End If
        End Function
        Public Function Download(ByVal unmanagedIDs As IEnumerable(Of String)) As QuoteOptionsResponse
            If unmanagedIDs Is Nothing Then : Throw New ArgumentNullException("unmanagedIDs", "The passed list is null.")
            Else : Return Me.ToResponse(MyBase.DownloadStream(Me.DownloadURL(unmanagedIDs)))
            End If
        End Function
        Public Function Download(ByVal unmanagedID As String, ByVal expirationDate As Date) As QuoteOptionsResponse
            If unmanagedID = String.Empty Then : Throw New ArgumentNullException("unmanagedID", "The passed ID is empty.")
            Else : Return Me.ToResponse(MyBase.DownloadStream(Me.DownloadURL(New String() {unmanagedID}, New Date() {expirationDate})))
            End If
        End Function
        Public Function Download(ByVal unmanagedIDs As IEnumerable(Of String), ByVal expirationDate As Date) As QuoteOptionsResponse
            If unmanagedIDs Is Nothing Then : Throw New ArgumentNullException("unmanagedIDs", "The passed list is null.")
            Else : Return Me.ToResponse(MyBase.DownloadStream(Me.DownloadURL(unmanagedIDs, New Date() {expirationDate})))
            End If
        End Function
        Public Function Download(ByVal unmanagedID As String, ByVal expirationDates As IEnumerable(Of Date)) As QuoteOptionsResponse
            If unmanagedID = String.Empty Then : Throw New ArgumentNullException("unmanagedID", "The passed ID is empty.")
            Else : Return Me.ToResponse(MyBase.DownloadStream(Me.DownloadURL(New String() {unmanagedID}, expirationDates)))
            End If
        End Function



        Public Overloads Sub DownloadAsync(ByVal managedID As IID, Optional ByVal userArgs As Object = Nothing)
            If managedID Is Nothing Then : Throw New ArgumentNullException("managedID", "The passed ID is null.")
            Else : Me.DownloadAsync(managedID.ID)
            End If
        End Sub
        Public Overloads Sub DownloadAsync(ByVal managedIDs As IEnumerable(Of IID), Optional ByVal userArgs As Object = Nothing)
            If managedIDs Is Nothing Then : Throw New ArgumentNullException("managedIDs", "The passed list is null.")
            Else : Me.DownloadAsync(mFinanceHelper.IIDsToStrings(managedIDs))
            End If
        End Sub
        Public Overloads Sub DownloadAsync(ByVal managedID As IID, ByVal expirationDate As Date, Optional ByVal userArgs As Object = Nothing)
            If managedID Is Nothing Then : Throw New ArgumentNullException("managedID", "The passed ID is null.")
            Else : Me.DownloadAsync(managedID.ID, expirationDate)
            End If
        End Sub
        Public Overloads Sub DownloadAsync(ByVal managedIDs As IEnumerable(Of IID), ByVal expirationDate As Date, Optional ByVal userArgs As Object = Nothing)
            If managedIDs Is Nothing Then : Throw New ArgumentNullException("managedIDs", "The passed list is null.")
            Else : Me.DownloadAsync(mFinanceHelper.IIDsToStrings(managedIDs), expirationDate)
            End If
        End Sub
        Public Overloads Sub DownloadAsync(ByVal managedID As IID, ByVal expirationDates As IEnumerable(Of Date), Optional ByVal userArgs As Object = Nothing)
            If managedID Is Nothing Then : Throw New ArgumentNullException("managedID", "The passed ID is null.")
            Else : Me.DownloadAsync(managedID.ID, expirationDates)
            End If
        End Sub

        Public Overloads Sub DownloadAsync(ByVal unmanagedID As String, Optional ByVal userArgs As Object = Nothing)
            If unmanagedID = String.Empty Then : Throw New ArgumentNullException("unmanagedID", "The passed ID is empty.")
            Else
                Dim args As New AsyncDownloadArgs(userArgs, New String() {unmanagedID}, New Date() {})
                MyBase.DownloadStreamAsync(Me.DownloadURL(args.IDs, args.ExpirationDates), args)
            End If
        End Sub
        Public Overloads Sub DownloadAsync(ByVal unmanagedIDs As IEnumerable(Of String), Optional ByVal userArgs As Object = Nothing)
            If unmanagedIDs Is Nothing Then : Throw New ArgumentNullException("unmanagedIDs", "The passed list is null.")
            Else
                Dim args As New AsyncDownloadArgs(userArgs, mHelper.EnumToArray(unmanagedIDs), New Date() {})
                MyBase.DownloadStreamAsync(Me.DownloadURL(args.IDs, args.ExpirationDates), args)
            End If
        End Sub
        Public Overloads Sub DownloadAsync(ByVal unmanagedID As String, ByVal expirationDate As Date, Optional ByVal userArgs As Object = Nothing)
            If unmanagedID = String.Empty Then : Throw New ArgumentNullException("unmanagedID", "The passed ID is empty.")
            Else
                Dim args As New AsyncDownloadArgs(userArgs, New String() {unmanagedID}, New Date() {expirationDate})
                MyBase.DownloadStreamAsync(Me.DownloadURL(args.IDs, args.ExpirationDates), args)
            End If
        End Sub
        Public Overloads Sub DownloadAsync(ByVal unmanagedIDs As IEnumerable(Of String), ByVal expirationDate As Date, Optional ByVal userArgs As Object = Nothing)
            If unmanagedIDs Is Nothing Then : Throw New ArgumentNullException("unmanagedIDs", "The passed list is null.")
            Else
                Dim args As New AsyncDownloadArgs(userArgs, mHelper.EnumToArray(unmanagedIDs), New Date() {expirationDate})
                MyBase.DownloadStreamAsync(Me.DownloadURL(args.IDs, args.ExpirationDates), args)
            End If
        End Sub
        Public Overloads Sub DownloadAsync(ByVal unmanagedID As String, ByVal expirationDates As IEnumerable(Of Date), Optional ByVal userArgs As Object = Nothing)
            If unmanagedID = String.Empty Then : Throw New ArgumentNullException("unmanagedID", "The passed ID is empty.")
            Else
                Dim args As New AsyncDownloadArgs(userArgs, New String() {unmanagedID}, mHelper.EnumToArray(expirationDates))
                MyBase.DownloadStreamAsync(Me.DownloadURL(args.IDs, args.ExpirationDates), args)
            End If
        End Sub



        ''' <summary>
        ''' Default constructor
        ''' </summary>
        ''' <remarks></remarks>
        Public Sub New()
        End Sub

        Private Sub DownloadAsync_Completed(ByVal sender As Base.Download, ByVal e As Base.StreamDownloadCompletedEventArgs) Handles MyBase.AsyncStreamDownloadCompleted
            If e IsNot Nothing AndAlso e.UserArgs IsNot Nothing AndAlso TypeOf e.UserArgs Is AsyncDownloadArgs Then
                Dim dlArgs As AsyncDownloadArgs = DirectCast(e.UserArgs, AsyncDownloadArgs)
                Dim args As New QuoteOptionsDownloadCompletedEventArgs(dlArgs.UserArgs, Me.ToResponse(e.Response), dlArgs.IDs)
                RaiseEvent AsyncDownloadCompleted(Me, args)
            End If
        End Sub

        Private Function DownloadURL(ByVal unmanagedIDs As IEnumerable(Of String), Optional ByVal expirationDates As IEnumerable(Of Date) = Nothing) As String
            Dim ids() As String = mHelper.EnumToArray(unmanagedIDs)
            If ids.Length > 0 Then

                Dim whereClause As New Text.StringBuilder

                If ids.Length = 1 Then
                    whereClause.AppendFormat("symbol=""{0}""", ids(0))
                Else
                    whereClause.Append("symbol in (")
                    For Each id As String In ids
                        If id.Trim <> String.Empty Then
                            whereClause.AppendFormat("""{0}"",", mHelper.CleanYqlParam(id))
                        End If
                    Next
                    whereClause.Remove(whereClause.Length - 1, 1)
                    whereClause.Append(")"c)
                End If

                If expirationDates IsNot Nothing Then
                    Dim expirations() As Date = mHelper.EnumToArray(expirationDates)
                    If ids.Length > 1 And expirations.Length > 1 Then Throw New NotSupportedException("Multiple IDs and multiple Expiration Dates are not supported")
                    If expirations.Length > 0 Then
                        If expirations.Length = 1 Then
                            whereClause.AppendFormat(" AND expiration=""{0}-{1:00}""", expirations(0).Year, expirations(0).Month)
                        Else
                            whereClause.Append(" AND expiration in (")
                            For Each exp As Date In expirations
                                whereClause.AppendFormat("""{0}-{1:00}"",", exp.Year, exp.Month)
                            Next
                            whereClause.Remove(whereClause.Length - 1, 1)
                            whereClause.Append(")"c)
                        End If
                    End If
                End If
                Return mHelper.YqlUrl("*"c, "yahoo.finance.options", whereClause.ToString, Nothing, False)
            Else
                Throw New NotSupportedException("An empty id list will not be supported.")
            End If
        End Function

        Private Function ToResponse(ByVal resp As Base.StreamResponse) As QuoteOptionsResponse
            Dim options As New List(Of QuoteOptionDataChain)

            Dim culture As New Globalization.CultureInfo("en-US")
            Dim mainLst = mHelper.GetResultsFromXml(resp.Result, "optionsChain")

            For Each chain In mainLst
                Dim idAtt = chain.Attributes("symbol")
                Dim expirationDateAtt = chain.Attributes("expiration")
                Dim expirationDate As Date
                If expirationDateAtt IsNot Nothing Then
                    If Not Date.TryParseExact(expirationDateAtt.Value, "yyyy-MM-dd", culture, Globalization.DateTimeStyles.None, expirationDate) Then
                        Date.TryParseExact(expirationDateAtt.Value, "yyyy-MM", culture, Globalization.DateTimeStyles.None, expirationDate)
                    End If
                End If
                Dim lst As New List(Of QuoteOptionData)
                For Each optionNode As XmlNode In chain.ChildNodes
                    If optionNode.Name = "option" Then
                        Dim opt As QuoteOptionData = mXmlParser.ToQuoteOption(optionNode, culture)
                        If opt IsNot Nothing Then lst.Add(opt)
                    End If
                Next
                options.Add(New QuoteOptionDataChain(idAtt.Value, expirationDate, lst))
            Next
            Return New QuoteOptionsResponse(resp.Connection, options.ToArray)
        End Function

        Private Class AsyncDownloadArgs
            Inherits Base.DownloadEventArgs
            Public IDs() As String
            Public ExpirationDates() As Date
            Public Sub New(ByVal userArgs As Object, ByVal id() As String, ByVal dates() As Date)
                MyBase.New(userArgs)
                Me.IDs = id
                Me.ExpirationDates = dates
            End Sub
        End Class

    End Class

    ''' <summary>
    ''' Stores the received quote informations of an asynchronous download.
    ''' </summary>
    ''' <remarks></remarks>
    Public Class QuoteOptionsDownloadCompletedEventArgs
        Inherits Base.DownloadCompletedEventArgs

        Private mIDs() As String

        ''' <summary>
        ''' The requested IDs of the download.
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property IDs() As IEnumerable(Of String)
            Get
                Return mIDs
            End Get
        End Property
        ''' <summary>
        ''' Gets the response with quote options.
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Overloads ReadOnly Property Response() As QuoteOptionsResponse
            Get
                Return DirectCast(MyBase.Response, QuoteOptionsResponse)
            End Get
        End Property

        Friend Sub New(ByVal userArgs As Object, ByVal resp As QuoteOptionsResponse, ByVal ids() As String)
            MyBase.New(userArgs, resp)
            mIDs = ids
        End Sub

    End Class

    ''' <summary>
    ''' Provides information and response of an asynchronous quote otions download.
    ''' </summary>
    ''' <remarks></remarks>
    Public Class QuoteOptionsResponse
        Inherits Base.Response

        ''' <summary>
        ''' Gets the received quote options.
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Overloads ReadOnly Property Result() As QuoteOptionDataChain()
            Get
                Return DirectCast(MyBase.Result, QuoteOptionDataChain())
            End Get
        End Property

        Friend Sub New(ByVal info As Base.ConnectionInfo, ByVal result As QuoteOptionDataChain())
            MyBase.New(info, result)
        End Sub

    End Class



End Namespace